package com.liuyi.netty.im.lychat.modular.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liuyi.netty.im.commons.codes.CodeConst;
import com.liuyi.netty.im.commons.codes.data.event.Message;
import com.liuyi.netty.im.commons.codes.data.event.message.playload.Playload;
import com.liuyi.netty.im.lychat.LychatConst;
import com.liuyi.netty.im.lychat.modular.caches.GroupMemberCache;
import com.liuyi.netty.im.lychat.modular.caches.MessageRecordsCache;
import com.liuyi.netty.im.lychat.modular.entity.GroupMember;
import com.liuyi.netty.im.lychat.modular.entity.MessageRecords;
import com.liuyi.netty.im.lychat.modular.entity.MessageUserRecords;
import com.liuyi.netty.im.lychat.modular.enums.MessageFlow;
import com.liuyi.netty.im.lychat.modular.enums.MessageStatus;
import com.liuyi.netty.im.lychat.modular.enums.MessageType;
import com.liuyi.netty.im.lychat.modular.enums.SessionType;
import com.liuyi.netty.im.lychat.modular.mapper.MessageUserRecordsMapper;
import com.liuyi.netty.im.lychat.modular.service.IGroupMemberService;
import com.liuyi.netty.im.lychat.modular.service.IMessageRecordsService;
import com.liuyi.netty.im.lychat.modular.service.IMessageUserRecordsService;
import com.liuyi.netty.im.lychat.modular.service.IUserFriendService;
import com.liuyi.netty.im.server.util.ImHelp;
import com.liuyi.netty.im.server.util.ImUtil;
import com.liuyi.rapid.develop.tool.unusual.BizException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 消息用户记录表 服务实现类
 * </p>
 *
 * @author mybatis-plus
 * @since 2021-12-29
 */
@Service
public class MessageUserRecordsServiceImpl extends ServiceImpl<MessageUserRecordsMapper, MessageUserRecords> implements IMessageUserRecordsService {

    @Resource
    private IMessageRecordsService messageRecordsService;
    @Resource
    private IGroupMemberService groupMemberService;
    @Resource
    private IUserFriendService userFriendService;

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public MessageRecords createMessage(String fromId, String toId, SessionType sessionType, MessageType contentType, Object content) {
        if (!fromId.equals(LychatConst.SYS_USER_ID)) {
            switch (sessionType) {
                case C2C:
                    if (!userFriendService.checkFriend(fromId, toId)) {
                        ImUtil.sysMessage(LychatConst.sessionId(toId, sessionType), fromId, "对方不是您的好友，消息发送失败");
                        throw new BizException("抱歉，对方不是您的好友，消息发送失败。");
                    }
                    break;
                case GROUP:
                    if (!groupMemberService.checkGroupMember(fromId, toId)) {
                        ImUtil.sysMessage(LychatConst.sessionId(toId, sessionType), fromId, "您不是该群组成员，不可发言");
                        throw new BizException("抱歉，您不是该群组成员，不可发言。");
                    }
                    break;
            }
        }
        MessageRecords message = messageRecordsService.createMessage(fromId, toId, sessionType, contentType, content);
        //保存用户消息
        switch (sessionType) {
            case C2C:
                createC2CMessage(message.getId(), fromId, toId);
                break;
            case GROUP:
                createGroupMessage(message.getId(), fromId, toId);
                break;
            default:
                throw new BizException("会话类型错误.");
        }
        return message;
    }

    @Override
    public void sendMessage(MessageRecords messageRecords) {
        if (messageRecords == null) {
            throw new BizException("消息不存在，请先创建消息。");
        }
        if (messageRecords.getSendIs()) {
            //消息吊起发送，不要再次触发发送
            return;
        }
        //更新消息发送标识
        Long messageId = messageRecords.getId();
        messageRecordsService.updateSendIs(messageId);
        CodeConst.MsgType msgType = messageRecords.getContentType().msgType();
        Playload playload = ImUtil.decodePlayload(messageRecords.getContent(), msgType.getTClass());
        //保存并发送消息
        switch (messageRecords.getSessionType()) {
            case C2C:
                ImUtil.c2cMessage(messageId.toString(), messageRecords.getCreateTimeUnix(), messageRecords.getFromId(), messageRecords.getToId(), msgType, playload);
                break;
            case GROUP:
                ImUtil.groupMessage(messageId.toString(), messageRecords.getCreateTimeUnix(), messageRecords.getFromId(), messageRecords.getToId(), msgType, playload);
                break;
            default:
                throw new BizException("会话类型错误.");
        }
    }

    @Override
    public void sendMessage(Long messageId) {
        MessageRecordsCache messageRecordsCache = messageRecordsService.cache();
        MessageRecords messageRecords = messageRecordsCache.getById(messageId);
        sendMessage(messageRecords);
    }

    @Override
    public List<Message<? extends Playload>> wrapperMessage(List<MessageUserRecords> messageUserRecords) {
        List<Message<? extends Playload>> result = new ArrayList<>();
        messageUserRecords.forEach(item -> {
            Message<? extends Playload> message = wrapperMessage(item);
            if (message != null) {
                result.add(message);
            }
        });
        return result;
    }

    @Override
    public Message<? extends Playload> wrapperMessage(MessageUserRecords messageUserRecords) {
        MessageRecords messageRecord = messageRecordsService.cache().getById(messageUserRecords.getMessageId());
        if (messageRecord == null) {
            return null;
        }
        MessageType messageType = messageRecord.getContentType();
        Message<Playload> message = new Message<>();
        message.setSessionType(messageUserRecords.getSessionType().sessionType());
        String toId = ImHelp.sessionIdToId(messageUserRecords.getSessionId());
        switch (messageUserRecords.getSessionType()) {
            case GROUP:
                message.setFromProfile(groupMemberService.groupProfile(messageUserRecords.getFromId(), toId));
                break;
            case C2C:
                message.setFromProfile(userFriendService.c2cProfile(messageUserRecords.getFromId(), toId));
                break;
        }
        message.setSessionId(messageUserRecords.getSessionId());
        message.setMsgFlow(messageUserRecords.getFlow().msgFlow());
        message.setToId(messageUserRecords.getUserId());
        message.setFromId(messageUserRecords.getFromId());
        message.setMessageId(messageUserRecords.getMessageId().toString());
        message.setMsgType(messageType.msgType());
        Playload playload = ImUtil.decodePlayload(messageRecord.getContent(), messageType.msgType().getTClass());
        message.setPlayload(playload);
        message.setCreateTime(messageUserRecords.getCreateTimeUnix());
        return message;
    }

    @Override
    public Message<? extends Playload> wrapper(MessageRecords messageRecords, String fromId, String sessionId) {
        String toId = ImHelp.sessionIdToId(sessionId);
        CodeConst.SessionType sessionType = ImHelp.sessionIdToType(sessionId);
        Message<Playload> message = new Message<>();
        message.setSessionType(sessionType);
        switch (sessionType) {
            case GROUP:
                message.setFromProfile(groupMemberService.groupProfile(fromId, toId));
                break;
            case C2C:
                message.setFromProfile(userFriendService.c2cProfile(fromId, fromId));
                break;
        }
        message.setSessionId(sessionId);
        message.setMsgFlow(CodeConst.MsgFlow.OUT);
        message.setToId(toId);
        message.setFromId(fromId);
        message.setMessageId(messageRecords.getId().toString());
        message.setMsgType(messageRecords.getContentType().msgType());
        Playload playload = ImUtil.decodePlayload(messageRecords.getContent(), messageRecords.getContentType().msgType().getTClass());
        message.setPlayload(playload);
        message.setCreateTime(System.currentTimeMillis());
        return message;
    }

    private void createC2CMessage(Long messageId, String fromId, String toId) {
        //记录双方的，放松方 or 接收方
        MessageUserRecords fromUserRecords = new MessageUserRecords();
        fromUserRecords.setMessageId(messageId).setFromId(fromId).setUserId(fromId).setSessionId(ImHelp.sessionId(CodeConst.SessionType.C2C, toId))
                .setSessionType(SessionType.C2C).setFlow(MessageFlow.OUT).setMessageStatus(MessageStatus.SUCCESS);
        MessageUserRecords toUserRecords = new MessageUserRecords();
        toUserRecords.setMessageId(messageId).setFromId(fromId).setUserId(toId).setSessionId(ImHelp.sessionId(CodeConst.SessionType.C2C, fromId))
                .setSessionType(SessionType.C2C).setFlow(MessageFlow.IN).setMessageStatus(MessageStatus.NOT_SEND);
        this.saveBatch(CollUtil.newArrayList(fromUserRecords, toUserRecords));
    }

    private void createGroupMessage(Long messageId, String fromId, String groupId) {
        //记录所有群组成员的
        GroupMemberCache groupMemberCache = groupMemberService.cache();
        List<GroupMember> groupMembers = groupMemberCache.listByGroupId(groupId);
        if (groupMembers.isEmpty()) {
            throw new BizException("抱歉，该群组错误，无群组成员");
        }
        String sessionId = ImHelp.sessionId(CodeConst.SessionType.GROUP, groupId);
        List<MessageUserRecords> userRecords = new ArrayList<>();
        groupMembers.forEach(member -> {
            MessageUserRecords fromUserRecords = new MessageUserRecords();
            MessageFlow flow = MessageFlow.IN;
            MessageStatus messageStatus = MessageStatus.NOT_SEND;
            String userId = member.getUserId();
            if (userId.equals(fromId)) {
                flow = MessageFlow.OUT;
                messageStatus = MessageStatus.SUCCESS;
            }
            fromUserRecords.setMessageId(messageId).setFromId(fromId).setUserId(userId).setSessionId(sessionId)
                    .setSessionType(SessionType.GROUP).setFlow(flow).setMessageStatus(messageStatus);
            userRecords.add(fromUserRecords);
        });
        this.saveBatch(userRecords);
    }
}
